package beer.coder.walawala.gui.plugin;

import beer.coder.walawala.common.logger.Log;
import beer.coder.walawala.common.plugin.IPlugin;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.LinkedList;
import java.util.List;
import java.util.jar.JarFile;

/**
 * TODO
 *
 * @author amath0312
 * @version 1.0
 * @date 2020-04-19 13:44
 */
public class PluginLoader {

  public static final String LOG_TAG = "PLUGIN_LOADER";

  protected URLClassLoader classLoader;
  protected URL[] pluginJarFiles;
  protected List<Class> pluginClassList;

  public PluginLoader(URLClassLoader classLoader, URL[] pluginJarFiles) {
    this.classLoader = classLoader;
    this.pluginJarFiles = pluginJarFiles;
    this.pluginClassList = new LinkedList<>();
  }

  public PluginLoader() {
  }

  public Class loadClass(String className) throws ClassNotFoundException {
    return classLoader.loadClass(className);
  }

  public PluginWrapper createPlugin(Class cls) throws IllegalAccessException, InstantiationException {
    if (cls == null) {
      return null;
    }
    Object obj = cls.newInstance();
    if (IPlugin.class.isInstance(obj)) {
      IPlugin plugin = (IPlugin) obj;
      PluginWrapper wrapper = new PluginWrapper(plugin);
      return wrapper;
    }
    return null;
  }

  public void loadAllPluginClasses() throws IOException {
    for (URL jarFile : this.getPluginJarFiles()) {
      JarFile jar = new JarFile(new File(jarFile.getFile()));
      jar.stream().forEach(entry -> {
        Log.d(LOG_TAG, "find file: " + entry.getName());
        if (entry.getName().endsWith(".class")) {
          String className = entry.getName()
              .replace("/", ".")
              .replace("\\", ".")
              .replace(".class", "");
          try {
            this.loadPluginClass(className);
          } catch (Exception e) {
            Log.e(LOG_TAG, "load plugin class error", e);
          }
        }
      });
    }
  }

  public Class loadPluginClass(String className) throws ClassNotFoundException {
    Class cls = loadClass(className);
    if (IPlugin.class.isAssignableFrom(cls)) {
      this.pluginClassList.add(cls);
      return cls;
    } else {
      return null;
    }
  }

  public List<Class> getPluginClassList() {
    return pluginClassList;
  }

  public URL[] getPluginJarFiles() {
    return pluginJarFiles;
  }

  public void setPluginJarFiles(URL[] pluginJarFiles) {
    this.pluginJarFiles = pluginJarFiles;
  }

  public URLClassLoader getClassLoader() {
    return classLoader;
  }

  public void setClassLoader(URLClassLoader classLoader) {
    this.classLoader = classLoader;
  }
}
